using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace FileUtility.FTP.FTP
{
	/// <summary>
	/// The purpose of this class is to provide a single instance of constants necessary 
	/// for FTP.
	/// </summary>
	/// <remarks>
	/// Programmer:			Edmond Woychowsky
	/// Creation Date:		September 10, 2004
	/// Last Modification:	
	/// </remarks>
	public class FTP 
	{
		#region constants
		#region commands
		protected const string ABOR = "ABOR ";		//	Abort current file transfer
		protected const string ACCT = "ACCT ";		//	Set account information
		protected const string APPE = "APPE ";		//	Append to end of file
		protected const string CDUP = "CDUP ";		//	Parent directory
		protected const string CWD = "CWD  ";		//	Change working directory
		protected const string DELE = "DELE ";		//	Delete remote file
		protected const string HELP = "HELP ";		//	FTP command help
		protected const string LIST = "LIST ";		//	File list
		protected const string MDTM = "MDTM ";		//	File modified date/time
		protected const string MODE = "MODE ";		//	Mode
		protected const string MKD = "MKD  ";		//	Make remote directory
		protected const string NLST = "NLST ";		//	Filtered file list
		protected const string NOOP = "NOOP ";		//	No operation
		protected const string PASS = "PASS ";		//	Remove server password
		protected const string PASV = "PASV ";		//	Enter passive mode
		protected const string PORT = "PORT ";		//	Specify data port
		protected const string PWD = "PWD  ";		//	Print working directory
		protected const string QUIT = "QUIT ";		//	Terminate server connection
		protected const string REIN = "REIN ";		//	Reinitialize host connection
		protected const string RETR = "RETR ";		//	Retreive remote file
		protected const string RMD = "RMD  ";		//	Remove remote directory
		protected const string RNFR = "RNFR ";		//	Rename remote file from
		protected const string RNTO = "RNTO ";		//	Rename remote file to
		protected const string SITE = "SITE ";		//	Execute site specific command
		protected const string SIZE = "SIZE ";		//	Remote file size
		protected const string STAT = "STAT ";		//	Host status
		protected const string STOR = "STOR ";		//	Store file on remote host
		protected const string STOU = "STOU ";		//	Store uniquely named file
		protected const string STRU = "STRU ";		//	Set transfer file structure
		protected const string SYST = "SYST ";		//	Get host system type
		protected const string TYPE = "TYPE ";		//	Set transfer type
		protected const string USER = "USER ";		//	Remove server user id
		protected const string CONNECT = "CONNE";	//	Connect
		protected const string DATA = "XYZZY";		//	No command data transfer
		#endregion

		#region types
		public const string TYPE_ASCII = "A";		//	ASCII text
		public const string TYPE_EBCDIC = "E";		//	EBCDIC text
		public const string TYPE_IMAGE = "I";		//	Image (binary)
		public const string TYPE_LOCAL = "L";		//	Local format
		#endregion

		#region replyCodes
		public const int RESTART_MARKER = 110;		//	Restart marker reply
		public const int SERVICE_READY_IN = 120;	//	Service ready in x minutes
		public const int TRANSFER_STARTING = 125;	//	Data connection open
		public const int FILE_STATUS_OK = 150;		//	About to open data connection
		public const int COMMAND_OK = 200;			//	Command ok
		public const int COMMAND_SUPERFLUOUS = 202;	//	Command not implemented
		public const int STATUS_OK = 211;			//	System status/help reply
		public const int DIRECTORY_STATUS = 212;	//	Directory status reply
		public const int FILE_STATUS = 213;			//	File status reply
		public const int HELP_MESSAGE = 214;		//	Help message text
		public const int SYSTEM_TYPE = 215;			//	Official system type name
		public const int SERVICE_READY = 220;		//	Service ready for new user
		public const int SERVICE_CLOSING = 221;		//	Service closing connection
		public const int NO_TRANSFER = 225;			//	Data connection open
		public const int CONNECTION_CLOSING = 226;	//	Data connection closing
		public const int PASSIVE_MODE = 227;		//	Entering passive mode
		public const int LOGGED_IN = 230;			//	User logged in/out
		public const int ACTION_OK = 250;			//	Requested file action ok
		public const int PATHNAME = 257;			//	Pathname created
		public const int NEED_PASSWORD = 331;		//	Need user password
		public const int NEED_ACCOUNT = 332;		//	Need user account
		public const int PENDING = 350;				//	Action pending information
		public const int NOT_AVAILABLE = 421;		//	Not available, closing connection
		public const int CONNECTION_FAILED = 425;	//	Can't open data connection
		public const int TRANSFER_ABORTED = 426;	//	Connection closed
		public const int ACTION_NOT_TAKEN = 450;	//	File action not taken
		public const int ACTION_ABORTED = 451;		//	Action aborted, local error
		public const int FILE_UNAVAILABLE = 452;	//	Insufficient storage/file busy
		public const int SYNTAX_ERROR = 500;		//	Syntax error
		public const int ARGUMENT_ERROR = 501;		//	Syntax error in arguments
		public const int NOT_IMPLEMENTED = 502;		//	Command not implemented
		public const int COMMAND_SEQUENCE = 503;	//	Bad command squence
		public const int ARGUMENT = 504;			//	Argument not implemented
		public const int NOT_LOGGED_IN = 530;		//	Not logged in
		public const int NEED_ACCOUNT_FILES = 532;	//	Need acount for storing files
		public const int UNAVAILABLE = 550;			//	Action not taken
		public const int ABORTED_PAGE = 551;		//	Action aborted, page type unknown
		public const int ABORTED_STORAGE = 552;		//	Exceeded storage allocation
		public const int ABORTED_FILE_NAME = 553;	//	File name not allowed
		#endregion

		#region errorMessages
		protected const string CONNECTION_ERROR = "Connection to remote server failed";
		protected const string LOGON_ERROR = "Unable to logon to remote host";
		protected const string SOCKET_ERROR = "Unable to open socket";
		#endregion

		#region miscellaneous
		protected const int DEFAULT_PORT = 21;		//	Default FTP port
		protected int TIMEOUT = 5000;				//	Timeout 5 seconds
		protected const string CRLF = "\r\n";		//	Carriage return/line feed
		protected const string EOL = "1310";		//	Byte carriage return/line feed
		protected const string COMMA = ",";	
		protected const int RETRIES = 3;			//	Command retry attempts
		#endregion
		#endregion

		#region protected
		protected Encoding m_ascii = Encoding.ASCII;
		protected string m_uri;						//	Uniform Resource Identifier
		protected string m_user = "anonymous";		//	User id
		protected string m_password = "anonymous";	//	Password
		protected string m_transfer_type = "A";		//	Transfer type
		#endregion

		#region private
		private static MemoryStream m_traffic_stream =  new MemoryStream();
		private StreamWriter m_traffic = new StreamWriter(m_traffic_stream);
		private string m_ipAddress;					//	Data transfer IP address
		private int m_port = 20;					//	Data transfer port
		#endregion

		#region publicProperties
		/// <summary>
		/// The purpose of this property is to get/set the timeout value.
		/// </summary>
		public int timeout 
		{
			set 
			{
				this.TIMEOUT = int.Parse(value.ToString());
			}

			get 
			{
				return(this.TIMEOUT);
			}
		}

		/// <summary>
		/// The purpose of this property is to get a log of both the port 21 traffic 
		/// and the traffic from the data transfer port traffic.
		/// </summary>
		public string traffic 
		{
			get 
			{
				m_traffic_stream.Position = 0;

				StreamReader stream = new StreamReader(m_traffic_stream);

				return(stream.ReadToEnd());
			}
		}
		#endregion

		#region protectedProperties
		/// <summary>
		/// The purpose of this property is to set/get the IP address.
		/// </summary>
		protected string ipAddress 
		{
			get 
			{
				return(this.m_ipAddress);
			}
			set 
			{
				this.m_ipAddress = value.ToString();
			}
		}
		#endregion

		#region protectedMethods
		/// <summary>
		/// The purpose of this method is to maintain a log of all traffic for this 
		/// session.
		/// </summary>
		/// <param name="data">Information to log</param>
		protected void record(StringBuilder data) 
		{
			record(data.ToString());
		}

		/// <summary>
		/// The purpose of this method is to maintain a log of all traffic for this 
		/// session.
		/// </summary>
		/// <param name="data">Information to log</param>
		protected void record(string data) 
		{
			if(data.Length >= 5)
				if(data.Substring(0,5) == PASS)
				{
					this.m_traffic.Write(PASS);
					this.m_traffic.Write("********");
					this.m_traffic.Write(CRLF);
				}
				else
					this.m_traffic.Write(data);

			this.m_traffic.Flush();
		}

		/// <summary>
		/// The purpose of this method is to compute and store the port number for data 
		/// transfers.
		/// </summary>
		/// <param name="response">PASV or PORT command response</param>
		/// <returns>Port number</returns>
		protected int dataPort(string response) 
		{
			Regex re = new Regex(@"^.{0,}\(");		//	Regular expression
			StringBuilder ipAddress = new StringBuilder();
			string[] values;						//	Address parts
			string work;

			work = re.Replace(response,"");

			re = new Regex(@"\).{0,}\r\n$");

			work = re.Replace(work,"");

			re = new Regex(@",");

			values = re.Split(work);				//	Isolate address/port

			for(int i=0;i < 4;i++)					//	Store IP address
				if(i == 3)
					ipAddress.Append(values[i]);
				else 
				{
					ipAddress.Append(values[i]);
					ipAddress.Append(".");
				}

			return(dataPort(int.Parse(values[4]),int.Parse(values[5])));
		}

		/// <summary>
		/// The purpose of this method is to compute and store the port number for data 
		/// transfers.
		/// </summary>
		/// <param name="p1">Port number part 1</param>
		/// <param name="p2">Port number part 2</param>
		/// <returns>Port number</returns>
		protected int dataPort(string p1, string p2) 
		{
			return(dataPort(int.Parse(p1),int.Parse(p2)));
		}

		/// <summary>
		/// The purpose of this method is to compute and store the port number for data 
		/// transfers.
		/// </summary>
		/// <param name="p1">Port number part 1</param>
		/// <param name="p2">Port number part 2</param>
		/// <returns>Port number</returns>
		protected int dataPort(int p1,int p2) 
		{
			this.m_port = (p1 << 8) + p2;			//	Compute port number

			return(this.m_port);
		}

		/// <summary>
		/// The purpose of this method is to compute and store the port number for data 
		/// transfers.
		/// </summary>
		/// <returns>Port number</returns>
		protected int dataPort() 
		{
			return(this.m_port);
		}
		#endregion
	}
}
